package com.hmdp.utils;

import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 每个业务都可以创建他自己的锁，该业务的实现类及其调用类必须是多例的
 * 每个锁的value是用UUID+线程ID生成的，为了保证在分布式系统下的唯一标识
 * 由于bean是多例的，这样可以满足每个线程的UUID可以保存到Bean中，在释放锁时获取到每个线程的UUID进行比对
 */
public class SimpleRedisLock {
    private StringRedisTemplate stringRedisTemplate;
    private static final String KEY_PREFIX = "lock:";
    private String valueUUID;
    private String redisKey;
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取互斥锁
     *
     * @param timeOut  超时时间
     * @param timeUtil 超时单位
     * @return
     */
    public boolean trylock(String keyName, long timeOut, TimeUnit timeUtil) {
        redisKey = KEY_PREFIX + keyName;
        valueUUID = UUID.randomUUID().toString() + Thread.currentThread().getId();
        //获取锁
        Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, valueUUID, timeOut, timeUtil);
        return Boolean.TRUE.equals(isLock);//有三种情况true，false，null，我们需要null也返回false
    }

    /**
     * 使用lua脚本实现redis分布式锁的原子性
     */
    public void unlock() {
         stringRedisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(redisKey), valueUUID);
    }

//    /**
//     * 释放锁
//     */
//    public void unlock() {
//        if (valueUUID.equals(stringRedisTemplate.opsForValue().get(redisKey))) {
//            stringRedisTemplate.delete(redisKey);
//        }
//    }
}
